package com.sudo.demo.security.filter;

import com.sudo.demo.security.constant.AuthWhiteList;
import com.sudo.demo.security.constant.TokenConstant;
import com.sudo.demo.security.security.GrantedAuthorityImpl;
import com.sudo.demo.security.util.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 自定义JWT认证过滤器
 * 该类继承自BasicAuthenticationFilter，在doFilterInternal方法中，
 * 从http头的Authorization 项读取token数据，然后用Jwts包提供的方法校验token的合法性。
 * 如果校验通过，就认为这是一个取得授权的合法请求
 * @author zhaoxinguo on 2017/9/13.
 */
@Slf4j
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {

	public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 请求头中没有token则走内置验证 anyRequest().authenticated()
        String requestURI = request.getRequestURI();
        String token = request.getHeader(TokenConstant.AUTHORIZATION);
        if (!StringUtils.hasText(token) || !token.startsWith(TokenConstant.BEARER)) {
            chain.doFilter(request, response);
            return;
        }

        // 如果token不为空，并且是以指定票据开头
        // 请求头中存在token，则判断是否为白名单，白名单则直接走
        if (StringUtils.hasText(token) && token.startsWith(TokenConstant.BEARER)) {
            // 如果请求路径是放行路径，则直接跳过认证
            List<String> anonUrlList = Arrays.asList(AuthWhiteList.AUTH_WHITELIST);
            if (anonUrlList.contains(requestURI)) {
                chain.doFilter(request, response);
                return;
            }
        }

        // 请求头中存在token，且路径不是白名单，则走自定义验证
        UsernamePasswordAuthenticationToken authentication = getAuthentication(token);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(String token) {
        Claims claims = JwtUtil.parseToken(token);
        // parse the token.
        String user = claims.getSubject();
        if (user != null) {
            String[] userInfo = user.split(":");
            String username = userInfo[0];
            String[] authorityArr = userInfo[1].split(",");
            ArrayList<GrantedAuthority> authorities = new ArrayList<>();
            for (String authority : authorityArr) {
                authorities.add(new GrantedAuthorityImpl(authority));
            }
            return new UsernamePasswordAuthenticationToken(username, null, authorities);
        }
        return null;
    }

}